namespace FineUtil.PaperGeo
{

    /// <summary>
    /// Rectangle.
    /// </summary>
    public struct GeoRect
    {

        /// <summary>
        /// X-coordinate of the left side.
        /// </summary>
        public readonly int X;

        /// <summary>
        /// Y-coordinate of the top side.
        /// </summary>
        public readonly int Y;

        /// <summary>
        /// Width.
        /// </summary>
        public readonly int W;

        /// <summary>
        /// Height.
        /// </summary>
        public readonly int H;


        /// <summary>
        /// Trivial constructor.
        /// </summary>
        /// <param name="x">X-coordinate of the left side.</param>
        /// <param name="y">Y-coordinate of the top side.</param>
        /// <param name="w">width.</param>
        /// <param name="h">height.</param>
        public GeoRect(int x, int y, int w, int h)
        {
            X = x;
            Y = y;
            W = w;
            H = h;
        }
        
        /// <summary>
        /// Constructs a rectangle from a point and a size.
        /// </summary>
        /// <param name="point">left top point.</param>
        /// <param name="size">size.</param>
        public GeoRect(GeoPoint point, GeoSize size)
        {
            X = point.X;
            Y = point.Y;
            W = size.W;
            H = size.H;
        }


        /// <summary>
        /// Determinses that both width and height are positive.
        /// </summary>
        public bool Positive
        {
            get { return W > 0 && H > 0; }
        }
        
        
        /// <summary>
        /// Size of this rectangle.
        /// </summary>
        public GeoSize Size
        {
            get { return new GeoSize(W,H); }
        }
        
        
        /// <summary>
        /// Center of the rectangle.
        /// </summary>
        public GeoPoint Center
        {
            get
            {
                return new GeoPoint(X + W/2, Y + H/2);
            }
        }


        /// <summary>
        /// Left top corner point.
        /// </summary>
        public GeoPoint Corn_LT
        {
            get { return new GeoPoint(X,Y); }
        }

        
        /// <summary>
        /// Left bottom corner point.
        /// </summary>
        public GeoPoint Corn_LB
        {
            get { return new GeoPoint(X,Y+H-1); }
        }

        
        /// <summary>
        /// Right top corner point.
        /// </summary>
        public GeoPoint Corn_RT
        {
            get { return new GeoPoint(X+W-1,Y); }
        }

        
        /// <summary>
        /// Right bottom corner point.
        /// </summary>
        public GeoPoint Corn_RB
        {
            get { return new GeoPoint(X+W-1,Y+H-1); }
        }
        
        
        public GeoRect GetTopInset(GeoInsets insets)
        {
            return new GeoRect(X, Y, W, insets.T);
        }
        
        
        public GeoRect GetLeftInset(GeoInsets insets)
        {
            return new GeoRect(X, Y, insets.L, H);
        }


        public GeoRect GetBottomInset(GeoInsets insets)
        {
            return new GeoRect(X, Y + H - insets.B, W, insets.B);
        }


        public GeoRect GetRightInset(GeoInsets insets)
        {
            return new GeoRect(X + W - insets.R, Y, insets.R, H);
        }


        public GeoRect Shift(GeoSize delta)
        {
            return new GeoRect(X + delta.W, Y + delta.H, W, H);
        }


        
        
        /// <summary>
        /// Compares for equivalency.
        /// </summary>
        public bool Equals(GeoRect r2)
        {
            return this.X == r2.X && this.Y == r2.Y
                && this.W == r2.W && this.H == r2.H;
        }

        public override bool Equals(object obj)
        {
            return Equals((GeoRect) obj);
        }
        
        

        public static bool operator == (GeoRect r1, GeoRect r2)
        {
            return r1.X == r2.X && r1.Y == r2.Y
                && r1.W == r2.W && r1.H == r2.H;
        }
        
        
        public static bool operator != (GeoRect r1, GeoRect r2)
        {
            return r1.X != r2.X || r1.Y != r2.Y
                || r1.W != r2.W || r1.H != r2.H;
        }
        
        
        /// <summary>
        /// Makes a rect that an inner area of rect minus insets.
        /// </summary>
        /// <param name="rect">outer rect</param>
        /// <param name="insets">insets (that will be substracted)</param>
        /// <returns></returns>
        public static GeoRect operator - (GeoRect rect, GeoInsets insets)
        {
            return new GeoRect(rect.X + insets.L,             rect.Y + insets.T, 
                              rect.W - insets.L - insets.R,  rect.H - insets.T - insets.B);
        }


      
        
        /// <summary>
        /// Checks whether the specified point is in this rectangle.
        /// </summary>
        /// <param name="point">point to check.</param>
        /// <returns></returns>
        public bool Contains(GeoPoint point)
        {
            return this.X <= point.X && point.X <= this.X + this.W - 1
                && this.Y <= point.Y && point.Y <= this.Y + this.H - 1;
        }



        public override string ToString()
        {
            return "[" + X + ',' + Y + ',' + W + ',' + H + ']';
        }


        public override int GetHashCode()
        {
            return Y*37 + X*31 + H*5 + W*3;
        }
    }


}
